package uk.co.q3c.deplan.server.unit.domain.resource;

import java.util.ArrayList;

import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.Test;

import uk.co.q3c.deplan.client.domain.resource.IndividualResource;
import uk.co.q3c.deplan.client.domain.resource.InvalidResourceException;
import uk.co.q3c.deplan.client.domain.resource.PersistenceReferenceException;
import uk.co.q3c.deplan.client.domain.resource.Resource;
import uk.co.q3c.deplan.client.domain.resource.ResourceGroup;
import uk.co.q3c.deplan.client.domain.resource.ResourcePool;
import uk.co.q3c.deplan.client.service.ServiceFactory;
import uk.co.q3c.deplan.server.dao.Model;
import uk.co.q3c.deplan.server.unit.dao.LocalDatastoreTestCase;
import util.TestUtils;

import com.google.appengine.repackaged.com.google.common.collect.Lists;

@Test
public class ResourcePool_UT extends LocalDatastoreTestCase {
	IndividualResource r1;
	IndividualResource r2;
	IndividualResource r3;
	ResourcePool pool = null;
	transient protected final Logger logger = Logger.getLogger(getClass().getName());

	@Override
	protected void beforeMethod() throws Exception {
		super.beforeMethod();
		pool = new ResourcePool(connection);
		r1 = new IndividualResource(connection);
		r1.setName("r1");
		r1.save();
		r2 = new IndividualResource(connection);
		r2.setName("r2");
		r2.save();
		r3 = new IndividualResource(connection);
		r3.setName("r3");
		r3.save();
	}

	@Override
	protected void afterMethod() throws Exception {
		connection.closeSession();
		super.afterMethod();

	}

	/**
	 * Changed functionality - group is no longer created automatically when a call to groupContainingOnly fails
	 * 
	 * @throws PersistenceReferenceException
	 */
	public void createGroup() throws PersistenceReferenceException {
		pool.addResources(new IndividualResource[] { r1, r2, r3 });
		// ask for a group which doesn't exist
		ResourceGroup rg1 = pool.groupContainingOnly(Lists.newArrayList(new IndividualResource[] { r1, r2 }));
		Assert.assertNull(rg1);
		rg1 = new ResourceGroup(connection);
		rg1.setName(pool.createUniqueName());
		rg1.addResource(r1);
		rg1.addResource(r2);
		rg1.save();
		pool.addResource(rg1);
		Assert.assertEquals(rg1.getName(), "unnamed0");
		// make sure one is created
		Assert.assertTrue(rg1.contains(r1));
		Assert.assertTrue(rg1.contains(r2));
		Assert.assertEquals(rg1.size(), 2);
		Assert.assertEquals(pool.groupCount(), 1);
		Assert.assertEquals(rg1.getName(), "unnamed0");
		// ask for the same group content again
		ResourceGroup rg2 = pool.groupContainingOnly(Lists.newArrayList(new IndividualResource[] { r1, r2 }));
		// only one group should exist, another should not have been created
		Assert.assertEquals(pool.groupCount(), 1);
		// and the two groups should be the same
		Assert.assertEquals(rg1.getKey(), rg2.getKey());
		// go for a different group, with just one member added
		ResourceGroup rg3 = pool.groupContainingOnly(Lists.newArrayList(new IndividualResource[] { r1, r2, r3 }));
		// no group found
		Assert.assertNull(rg3);
		// create a second group
		rg3 = new ResourceGroup(connection);
		rg3.setName(pool.createUniqueName());
		rg3.addResource(r1);
		rg3.addResource(r2);
		rg3.addResource(r3);
		rg3.save();
		rg3.setName(pool.createUniqueName());
		pool.addResource(rg3);

		Assert.assertEquals(pool.groupCount(), 2);
		// with the next unique name
		Assert.assertEquals(rg3.getName(), "unnamed1");
		// this newest group should be different to the first
		Assert.assertNotSame(rg1, rg3);
		// call the same group by resource names
		ResourceGroup rg4 = pool.groupContainingOnlyNames(Lists.newArrayList(new String[] { "r1", "r2", "r3" }));
		// no new groups to be created
		Assert.assertEquals(pool.groupCount(), 2);
		Assert.assertTrue(rg4.contains(r1));
		Assert.assertTrue(rg4.contains(r2));
		Assert.assertTrue(rg4.contains(r3));
	}

	@Test(expectedExceptions = InvalidResourceException.class)
	public void groupWithUncontainedResource() throws PersistenceReferenceException {
		// create a group, with resource not in pool
		pool.individuals()
				.remove(r2);
		ResourceGroup group = pool.groupContainingOnly(Lists.newArrayList(new IndividualResource[] { r1, r2 }));

	}

	/**
	 * Make sure keys and instances are handled correctly for both groups and individuals
	 * 
	 * @throws PersistenceReferenceException
	 */
	public void addRemoveResources() throws PersistenceReferenceException {
		pool.addResources(new IndividualResource[] { r1, r2 });
		// key count
		Assert.assertEquals(pool.individualCount(), 2);
		// instance count
		Assert.assertEquals(pool.individuals()
				.size(), 2);

		pool.remove(r1);
		Assert.assertEquals(pool.individualCount(), 1);
		// instance count
		Assert.assertEquals(pool.individuals()
				.size(), 1);

		ResourceGroup g1 = new ResourceGroup(connection);
		ResourceGroup g2 = new ResourceGroup(connection);
		g1.save();
		g2.save();
		IndividualResource ir1 = new IndividualResource(connection);
		IndividualResource ir2 = new IndividualResource(connection);
		ir1.save();
		ir2.save();
		ArrayList<Resource> resources = new ArrayList<Resource>();
		resources.add(g1);
		resources.add(g2);
		resources.add(ir1);
		resources.add(ir2);
		pool.addResources(resources);
		Assert.assertEquals(pool.individualCount(), 3);
		Assert.assertEquals(pool.groupCount(), 2);
		pool.remove(g1);
		Assert.assertEquals(pool.individualCount(), 3);
		Assert.assertEquals(pool.groupCount(), 1);
	}

	public void defaultResource() throws PersistenceReferenceException {

		// add individual resources to pool so they can be used in group
		pool.addResources(new IndividualResource[] { r1, r2 });
		ResourceGroup group = new ResourceGroup(connection);
		group.save();
		pool.addResource(group);
		// rename it
		group.setName("SysDev");
		group.save();
		// and set is as default
		pool.setDefaultResourceName("SysDev");
		// ask for a null resource
		Resource resource = pool.getResource(null);
		// should get the default back
		Assert.assertEquals("SysDev", resource.getName());
	}

	@Test(expectedExceptions = { InvalidResourceException.class })
	public void defaultResourceNonExistent() {
		// set to a non existent name
		pool.setDefaultResourceName("numpty");
	}

	public void caseInsensitivity() throws PersistenceReferenceException {
		ResourceGroup g1 = new ResourceGroup(connection);
		g1.setName("aaaa");
		g1.save();
		pool.addResource(g1);
		Assert.assertEquals(pool.getResource("AaAa")
				.getKey(), g1.getKey());

		IndividualResource ir = new IndividualResource(connection);
		ir.setName("bbbb");
		ir.save();
		pool.addResource(ir);
		Assert.assertEquals(ir.getKey(), pool.getResource("BBbb")
				.getKey());
	}

	/**
	 * There was a bug with a rename only affecting the Resource object, but not updating the associated map key. This was because the
	 * listener was not being established for the map after loading back from the database <br>
	 * <br>
	 * Test removed, no longer using a map
	 * 
	 * @throws PersistenceReferenceException
	 * 
	 */
	@Test(enabled = false)
	public void renameMapEntry() throws PersistenceReferenceException {
		ServiceFactory dbc = TestUtils.dbc(connection, true);
		Model.setConnection(connection);
		ResourcePool pool = Model.getResourcePool();
		ResourceGroup group = new ResourceGroup(connection);
		group.setName("a");
		pool.addResource(group);
		// check the listener works to start with
		group.setName("a1");
		ResourceGroup group2 = (ResourceGroup) pool.getResource("a1");
		Assert.assertEquals(group, group2);
		pool.addResource(group);
		pool.save();

		// connection.close();
		// re-open database and reload the pool.
		// connection.open();
		Model.setConnection(connection);
		pool = Model.getResourcePool();
		group = (ResourceGroup) pool.getResource("a1");
		group.setName("a2");
		group2 = (ResourceGroup) pool.getResource("a2");
		Assert.assertEquals(group, group2);
		// connection.close();
		// testdao.delete();
	}
}
